﻿using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;

namespace CPN.PJN.Silverlight.Common.Helpers
{
    /// <summary>
    /// <see cref="UIElementExtensions"/> Extension des méthodes du FrameworkElement
    /// </summary>
    public static class UIElementExtensions
    {
        #region Visibility

        /// <summary>
        /// Renvoie si un contrôle est visible ou non
        /// </summary>
        public static bool IsVisible(this UIElement element)
        {
            if (element == null)
                return false;

            return element.Visibility == Visibility.Visible;
        }

        /// <summary>
        /// Change la visibilité d'un composant graphique
        /// </summary>
        /// <param name="element"></param>
        /// <param name="visibility"></param>
        public static void SetVisibility(this UIElement element, bool visibility)
        {
            if (element != null)
            {
                element.Visibility = visibility ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        #endregion

        #region Transforms
        /// <summary>
        /// Recherche le Transform d'un élément graphique
        /// S'il ne le trouve pas, il le créé et le renvoie
        /// </summary>
        /// <param name="target">Cible</param>
        /// <param name="type">Type de Transform</param>
        /// <returns></returns>
        public static T FindTransform<T>(this UIElement target) where T : Transform
        {
            TransformGroup tg = target.RenderTransform as TransformGroup;

            if (tg != null)
            {
                if (tg.Children.Count > 0)
                {
                    for (int i = 0; i < tg.Children.Count; i++)
                    {
                        if (tg.Children[i].GetType() == typeof(T))
                        {
                            return (T)tg.Children[i];
                        }
                    }
                }
            }
            else if (target.RenderTransform.GetType() == typeof(T))
            {
                return (T)target.RenderTransform;
            }
            else
            {
                tg = new TransformGroup();
            }

            Transform trans = null;

            if (typeof(T) == typeof(TranslateTransform))
            {
                trans = new TranslateTransform();
            }
            else if (typeof(T) == typeof(ScaleTransform))
            {
                trans = new ScaleTransform();
            }
            else if (typeof(T) == typeof(RotateTransform))
            {
                trans = new RotateTransform();
            }
            else if (typeof(T) == typeof(SkewTransform))
            {
                trans = new SkewTransform();
            }

            if (trans != null)
            {
                tg.Children.Add(trans);
                target.RenderTransform = tg;
            }

            return (T)trans;
        }
        
        #endregion

        #region Arbre visuel

        /// <summary>
        /// Recherche une ressource dans l'arbre visuel parent
        /// </summary>
        /// <param name="element">Element de départ de la recherche</param>
        /// <typeparam name="T">Type du parent recherché</typeparam>
        /// <returns>Ressource cherchée, null si non trouvée</returns>
        public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
        {
            DependencyObject parentObject = GetParentVisual(child);

            if (parentObject == null) return null;

            T parent = parentObject as T;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                return FindParent<T>(parentObject);
            }
        }

        /// <summary>
        /// Recherche une ressource dans l'arbre visuel parent
        /// </summary>
        /// <param name="element">Element de départ de la recherche</param>
        /// <param name="key">Clé de la ressource</param>
        /// <returns>Ressource cherchée, null si non trouvée</returns>
        public static object FindResource(this FrameworkElement element, string key)
        {
            while (element != null)
            {
                object value = element.Resources[key];
                if (value != null)
                {
                    return value;
                }

                element = GetParentVisual(element) as FrameworkElement;
            }

            return null;
        }

        private static DependencyObject GetParentVisual(DependencyObject element)
        {
            var parent = VisualTreeHelper.GetParent(element);
            if (parent == null && element is FrameworkElement)
            {
                parent = ((FrameworkElement)element).Parent;
            }

            return parent;
        }

        /// <summary>
        /// Tente de détecter si un élément est chargé ou non
        /// </summary>
        /// <param name="element"></param>
        /// <returns></returns>
        public static bool IsLoaded(this FrameworkElement element)
        {
#if SILVERLIGHT
            UIElement rootVisual = Application.Current.RootVisual;
            DependencyObject parent = element.Parent;
            if (parent == null)
            {
                parent = VisualTreeHelper.GetParent(element);
            }
            return ((parent != null) || ((rootVisual != null) && (element == rootVisual)));
#else
			return element.IsLoaded;
#endif
        }
        
        /// <summary>
        /// Récupère le premier enfant du type spécifié
        /// </summary>
        /// <typeparam name="T">Type à rechercher</typeparam>
        /// <param name="dpob">Objet à parcourir</param>
        /// <returns></returns>
        /// <remarks>Attention : la méthode parcourt tous les enfants de l'arbre visuel, méthode coûteuse donc</remarks>
        public static T FindChild<T>(this DependencyObject dpob) where T : DependencyObject
        {
            int count = VisualTreeHelper.GetChildrenCount(dpob);
            for (int i = 0; i < count; i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(dpob, i);
                T childAsT = child as T;
                if (childAsT != null)
                {
                    return childAsT;
                }
                childAsT = FindChild<T>(child);
                if (childAsT != null)
                {
                    return childAsT;
                }
            }
            return null;
        }

        /// <summary>
        /// Retire l'élément de l'arbre visuel
        /// </summary>
        /// <param name="element"></param>
        public static void RemoveFromParentPanel(this DependencyObject element)
        {
            UIElement removeObject = element as UIElement;

            if (removeObject != null)
            {
                DependencyObject parent = VisualTreeHelper.GetParent(removeObject) as Panel;
                if (parent == null && removeObject is FrameworkElement)
                    parent = ((FrameworkElement)removeObject).Parent;

                if (parent is Panel)
                {
                    ((Panel)parent).Children.Remove(removeObject);
                }
                else if (parent is ItemsControl)
                {
                    ((ItemsControl)parent).Items.Remove(removeObject);
                }
                else if (parent is ContentControl)
                {
                    ((ContentControl)parent).Content = null;
                }
            }
        }

        #endregion

        #region VisualStates

        public static Storyboard FindStoryboard(this FrameworkElement parent, string groupName, string stateName)
        {
            var vsgs = VisualStateManager.GetVisualStateGroups(parent);
            foreach (VisualStateGroup vsg in vsgs)
            {
                if (vsg.Name != groupName)
                    continue;
                foreach (VisualState vs in vsg.States)
                {
                    if (vs.Name == stateName)
                        return vs.Storyboard;
                }
            }
            return null;
        }

        #endregion
    }
}